<?php

declare(strict_types=1);

namespace Zms\Validate\Rule;

use Zms\Unit\Arr;

/**
 * 验证规则抽象类
 */
abstract class RuleAbstract
{
    /**
     * 错误信息数组
     * @var array
     */
    protected $_errors = [];

    /**
     * 默认错误消息
     * @var string
     */
    protected $_error = '{label}未通过验证';

    /**
     * 验证值
     * @var mixed
     */
    protected $_value;

    /**
     * 跳过验证标志
     * @var bool
     */
    protected $_noSkipping = false;

    /**
     * 验证器初始化
     *
     * @param array $config 配置项
     */
    public function __construct(array $config = [])
    {
        foreach ($config as $key => $value) {
            $method = 'set' . ucfirst($key);
            call_user_func_array([$this, $method], [$value]);
        }
    }

    /**
     * 直接调用规则验证
     *
     * @param mixed $value 待验证的值
     * @param array $config 配置项
     * @param string $label 字段标签，用于错误提示信息中
     * @return string|bool 成功返回true，失败返回第一个错误信息
     */
    public static function check($value, $config = [], $label = '内容')
    {
        $validate = new static($config);
        if ($validate->validate($value, $label) === true) {
            return true;
        }
        return $validate->firstError();
    }

    /**
     * 抽象方法：具体的验证逻辑实现
     *
     * @param mixed $value 待验证的值
     * @param string $label 字段标签，用于错误提示信息中
     * @return bool 验证结果
     */
    abstract public function validate($value, string $label = ''): bool;

    /**
     * 设置默认错误消息
     *
     * @param string $error 错误信息
     * @return RuleAbstract 当前实例
     */
    public function setError(string $error): RuleAbstract
    {
        $this->_error = $error;
        return $this;
    }

    /**
     * 设置是否跳过空值验证
     *
     * @param bool $noSkipping 是否跳过
     * @return RuleAbstract 当前实例
     */
    public function setNoSkipping(bool $noSkipping): RuleAbstract
    {
        $this->_noSkipping = $noSkipping;
        return $this;
    }

    /**
     * 添加错误信息
     *
     * @param string $error 错误信息
     * @param string $label 字段标签，用于替换错误信息中的占位符
     * @return bool 返回false表示验证失败
     */
    public function addError(string $error, string $label = ''): bool
    {
        if (preg_match_all('#{(.*?)}#', $error, $all)) {
            foreach ($all[1] as $key) {
                if ($key === 'label') {
                    $error = str_replace('{label}', $label, $error);
                } else {
                    $attr = '_' . $key;
                    $error = str_replace('{' . $key . '}', strval($this->$attr ?? ''), $error);
                }
            }
        }
        $this->_errors[] = $error;
        return false;
    }

    /**
     * 获取第一条错误信息
     *
     * @return string 第一条错误信息
     */
    public function firstError(): string
    {
        if (count($this->_errors) > 0) {
            return Arr::firstValue($this->_errors);
        }
        return '';
    }

    /**
     * 获取最后一条错误信息
     *
     * @return string 最后一条错误信息
     */
    public function finalError(): string
    {
        if (count($this->_errors) > 0) {
            return Arr::finalValue($this->_errors);
        }
        return '';
    }

    /**
     * 获取所有错误信息
     *
     * @return array 所有错误信息
     */
    public function getErrors(): array
    {
        return $this->_errors;
    }

    /**
     * 判断是否有验证失败的情况
     *
     * @return bool 是否验证失败
     */
    public function isFail(): bool
    {
        return count($this->_errors) > 0;
    }

    /**
     * 获取验证后的值
     *
     * @return mixed 验证后的值
     */
    public function getValue()
    {
        return $this->_value;
    }
}